package com.xrui.hbase;

import com.google.common.base.Joiner;
import com.google.common.base.Preconditions;
import com.xrui.hbase.codec.RowKeyDecoder;
import com.xrui.hbase.codec.RowKeyEncoder;
import com.xrui.hbase.schema.RowKeyFormat;
import com.xrui.hbase.schema.TableLayout;
import com.xrui.hbase.util.ByteArrayFormatter;


import java.io.IOException;
import java.util.ArrayList;
import java.util.List;

/**
 * Entity ID encapsulating an HBase row key. This literally
 * represents a byte[] containing an hbase row key.
 */
public final class HBaseEntityId extends EntityId {
    private byte[] mHBaseRowKey;
    private List<Object> mRowKeyComponents = null;
    private RowKeyDecoder mRowKeyDecoder;
    private RowKeyFormat mRowKeyFormat;

    /**
     * Creates an HBaseEntityId from the specified HBase row key.
     *
     * @param hbaseRowKey HBase row key.
     */
    private HBaseEntityId(byte[] hbaseRowKey, TableLayout tableLayout) {
        mHBaseRowKey = hbaseRowKey;
        if (null == tableLayout) {
            mRowKeyDecoder = null;
            mRowKeyFormat = null;
        } else {
            mRowKeyDecoder = tableLayout.getRowKeyDecoder();
            mRowKeyFormat = tableLayout.getRowKeyFormat();
        }
    }

    /**
     * Creates a new entity id using the specified hbase row key. This should be used by
     * project developers, and not by end-users.
     *
     * @param hbaseRowKey that will back the new entity id.
     * @return a new entity id that uses the specified HBase row key.
     */
    public static HBaseEntityId create(byte[] hbaseRowKey, TableLayout tableLayout) {

        return new HBaseEntityId(hbaseRowKey, tableLayout);
    }

    public static HBaseEntityId create(TableLayout tableLayout, Object... rowKey)
        throws IOException {
        RowKeyEncoder rowKeyEncoder = tableLayout.getRowKeyEncoder();
        byte[] hbaseRowKey = rowKeyEncoder.encode(
            RowKeyComponents.fromComponents(rowKey),
            tableLayout.getRowKeyFormat()
        );
        return HBaseEntityId.create(hbaseRowKey, tableLayout);
    }

    /**
     * {@inheritDoc}
     */
    @Override
    public byte[] getHBaseRowKey() {
        return mHBaseRowKey;
    }

    /**
     * {@inheritDoc}
     */
    @Override
    public Object getComponentByIndex(int idx) throws IOException {
        Preconditions.checkArgument(idx >= 0);
        return getComponents().get(idx);
    }

    /**
     * {@inheritDoc}
     */
    @Override
    public List<Object> getComponents() throws IOException {
        if (null == mRowKeyComponents) {
            Object[] components = mRowKeyDecoder.decode(mHBaseRowKey, mRowKeyFormat).getComponents();
            mRowKeyComponents = new ArrayList<>();
            for (Object component : components) {
                mRowKeyComponents.add(component);
            }
        }
        return mRowKeyComponents;
    }

    /**
     * {@inheritDoc}
     */
    @Override
    public String toString() {
        String ret;
        String delimiter = mRowKeyFormat.getDelimiter();
        if (null == delimiter) {
            delimiter = "";
        }

        try {
            ret = Joiner.on(delimiter).join(getComponents());
        } catch (IOException e) {
            ret = e.toString();
        }
        return ret;
    }

    /**
     * Shell friendly string representation of the HBaseEntityId.
     * When used as input to a CLI, will be converted to the EntityId type specified in the
     * provided table layout.
     *
     * @return a copyable string.
     */
    @Override
    public String toShellString() {
        return String.format("hbase=hex:%s", ByteArrayFormatter.toHex(mHBaseRowKey));
    }

}
